home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
dev
/
devClientDev.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
15KB
|
534 lines
/*
* devClientDev.c --
*
* This file contains the code for the kernel to interact with
* a user process via a device. This is used to keep track of
* currently active client machines of the server's kernel.
*
* Copyright 1989 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/dev/devClientDev.c,v 9.3 92/12/13 18:11:54 mgbaker Exp $ SPRITE (Berkeley)";
#endif /* not lint */
#include <sprite.h>
#include <stdio.h>
#include <stdlib.h>
#include <sync.h>
#include <devClientDev.h>
#include <procTypes.h>
#include <timer.h>
#include <fsioDevice.h>
#include <recov.h>
#include <fsutil.h>
#define MAX_INDEX 100
typedef struct DeviceState {
Dev_ClientInfo toUserQueue[MAX_INDEX]; /* Data to daemon. */
Dev_ClientInfo toKernelQueue[MAX_INDEX]; /* Data from daemon. */
int userUnreadIndex; /* Unread by daemon. */
int userUnusedIndex; /* Unwritten by os. */
int kernelUnreadIndex; /* Unread by os. */
int kernelUnusedIndex; /* Unwritten by d. */
Fs_NotifyToken dataReadyToken; /* Read/write notify. */
Sync_Condition waitForDaemon; /* Wait for daemon. */
Boolean recovInProgress; /* Only one instance. */
Boolean waitingForRecov; /* Condition check. */
Sync_Condition waitForRecovResponse; /* Wait for reopen to
* client to return. */
} DeviceState;
static Boolean deviceOpen = FALSE;
static Fs_Device *theDevicePtr = (Fs_Device *) NULL;
#define INC_QUEUEPTR(theIndex) \
((theIndex) == (MAX_INDEX - 1) ? (theIndex) = 0 : (theIndex)++)
int currentUnreadIndex;
/*
* Need to do something about queue suddenly becoming empty because it's
* all filled up!
*/
static Sync_Lock clientStateLock;
#define LOCKPTR (&clientStateLock)
/*
*----------------------------------------------------------------------
*
* DevClientStateOpen --
*
* For the user process to open the device by which it communicates
* with the kernel about clients.
*
* Results:
* Return status.
*
* Side effects:
* State for device set up. Process created that will cause kernel
* to start recovering.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
DevClientStateOpen(devicePtr, useFlags, data, flagsPtr)
Fs_Device *devicePtr; /* Device info. */
int useFlags; /* Flags from stream being opened. */
Fs_NotifyToken data; /* Call-back for input notification. */
int *flagsPtr; /* OUT: Device flags. */
{
DeviceState *statePtr;
LOCK_MONITOR;
printf("Opening client state device.\n");
if (deviceOpen) {
printf("Client state device already open.\n");
UNLOCK_MONITOR;
return FS_FILE_BUSY;
}
theDevicePtr = devicePtr;
if ((useFlags & FS_USER) == 0) {
printf("DevClientStateOpen should only be called by a user proc.\n");
UNLOCK_MONITOR;
return GEN_INVALID_ARG;
}
statePtr = (DeviceState *) malloc(sizeof (DeviceState));
devicePtr->data = (ClientData) statePtr;
statePtr->userUnreadIndex = 0;
statePtr->userUnusedIndex = 0;
statePtr->dataReadyToken = data;
statePtr->recovInProgress = FALSE;
statePtr->waitingForRecov = FALSE;
/*
* We're receiving recovery list from user-level daemon if the device
* is opened for write as well as read.
*/
if (useFlags & FS_WRITE) {
Recov_InitServerDriven();
}
deviceOpen = TRUE;
/* Should put in timeout here for getting recov info from client daemon. */
UNLOCK_MONITOR;
return SUCCESS;
}
/*
*----------------------------------------------------------------------
*
* DevClientStateClose --
*
* Close the device so it can be reopened.
*
* Results:
* Return status.
*
* Side effects:
* Free space and such.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
DevClientStateClose(devicePtr, useFlags, openCount, writerCount)
Fs_Device *devicePtr; /* Device info. */
int useFlags; /* Flags from stream being opened. */
int openCount;
int writerCount;
{
LOCK_MONITOR;
printf("Closing client state device.\n");
if (!deviceOpen) {
printf("Client state device already closed.\n");
UNLOCK_MONITOR;
return SUCCESS;
}
free((char *) devicePtr->data);
deviceOpen = FALSE;
Recov_StopServerDriven();
UNLOCK_MONITOR;
return SUCCESS;
}
/*
*----------------------------------------------------------------------
*
* DevClientStateRead --
*
* A user process reads info from kernel on device about client states.
*
* Results:
* SUCCESS - if info was found in the queue.
* FS_WOULD_BLOCK - no info found.
*
* Side effects:
* Removes some of the info from the queue.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
DevClientStateRead(devicePtr, readPtr, replyPtr)
Fs_Device *devicePtr;
Fs_IOParam *readPtr; /* Read parameter block */
Fs_IOReply *replyPtr; /* Return length and signal */
{
ReturnStatus status = SUCCESS;
register DeviceState *statePtr;
LOCK_MONITOR;
statePtr = (DeviceState *) devicePtr->data;
if (statePtr->userUnreadIndex == statePtr->userUnusedIndex) {
status = FS_WOULD_BLOCK;
UNLOCK_MONITOR;
return status;
}
if (sizeof (Dev_ClientInfo) > readPtr->length) {
status = GEN_INVALID_ARG;
UNLOCK_MONITOR;
return status;
}
bcopy(&(statePtr->toUserQueue[statePtr->userUnreadIndex]), readPtr->buffer,
sizeof (Dev_ClientInfo));
replyPtr->length = sizeof (Dev_ClientInfo);
INC_QUEUEPTR(statePtr->userUnreadIndex);
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* DevClientStateWrite --
*
* A user process writes info to kernel on device about state of hosts
* before crash.
*
* Results:
* SUCCESS or invalid arg.
*
* Side effects:
* Removes some of the info from the queue.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
DevClientStateWrite(devicePtr, writePtr, replyPtr)
Fs_Device *devicePtr;
Fs_IOParam *writePtr; /* Write parameter block */
Fs_IOReply *replyPtr; /* Return length and signal */
{
ReturnStatus status = SUCCESS;
register DeviceState *statePtr;
LOCK_MONITOR;
statePtr = (DeviceState *) devicePtr->data;
if (statePtr->recovInProgress) {
panic("DevClientStateWrite: only one instance of recovery allowed.\n");
}
if (writePtr->offset != 0) {
printf("DevClientStateWrite: write offset didn't start at 0: %d.\n",
writePtr->offset);
}
if ((writePtr->length % sizeof (Dev_ClientInfo)) != 0) {
printf(
"DevClientStateWrite: Write info not multiple of client structure.\n");
UNLOCK_MONITOR;
return GEN_INVALID_ARG;
}
statePtr->recovInProgress = TRUE;
bcopy(writePtr->buffer, &(statePtr->toKernelQueue[0]), writePtr->length);
statePtr->kernelUnreadIndex = 0;
statePtr->kernelUnusedIndex = writePtr->length / sizeof (Dev_ClientInfo);
if (statePtr->kernelUnusedIndex > MAX_INDEX) {
panic("DevClientStateWrite: too many clients!\n");
}
Recov_ServerStartingRecovery();
while (statePtr->kernelUnreadIndex != statePtr->kernelUnusedIndex) {
int clientID;
clientID =
statePtr->toKernelQueue[statePtr->kernelUnreadIndex].hostNumber;
if (statePtr->toKernelQueue[statePtr->kernelUnreadIndex].hostState !=
DEV_CLIENT_STATE_NEW_HOST) {
panic("DevClientStateWrite: Bad host state from daemon.");
}
Recov_MarkDoingServerRecovery(clientID);
status = Fsutil_DoServerRecovery(clientID);
#ifdef NOTDEF
XX Mark going through recovery.
Proc_CallFunc(Fsutil_DoServerRecovery, (ClientData) clientID, 0);
statePtr->waitingForRecov = TRUE;
while (statePtr->waitingForRecov) {
(void) Sync_Wait(&(statePtr->waitForRecovResponse), FALSE);
}
XX Mark not going through recovery.
#endif NOTDEF
Recov_UnmarkDoingServerRecovery(clientID);
INC_QUEUEPTR(statePtr->kernelUnreadIndex);
}
Recov_ServerFinishedRecovery();
statePtr->recovInProgress = FALSE;
replyPtr->length = writePtr->length;
UNLOCK_MONITOR;
return SUCCESS;
}
/*
*----------------------------------------------------------------------
*
* Dev_ClientStateWakeRecovery --
*
* Call-back for another module to rewaken us for us to keep on
* doing recovery.
*
* Results:
* None.
*
* Side effects:
* Wakeup signal.
*
*----------------------------------------------------------------------
*/
void
Dev_ClientStateWakeRecovery()
{
DeviceState *statePtr;
statePtr = (DeviceState *) theDevicePtr->data;
statePtr->waitingForRecov = FALSE;
Sync_Broadcast(&(statePtr->waitForRecovResponse));
return;
}
/*
*----------------------------------------------------------------------
*
* DevClientStateIOControl --
*
* For the device to tell the kernel it is done processing client state.
*
* Results:
* SUCCESS - the operation was successful.
* SYS_INVALID_ARG - bad command, or wrong buffer size.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
DevClientStateIOControl(devicePtr, ioctlPtr, replyPtr)
Fs_Device *devicePtr;
Fs_IOCParam *ioctlPtr;
Fs_IOReply *replyPtr;
{
DeviceState *statePtr;
ReturnStatus status = SUCCESS;
int numEntries;
statePtr = (DeviceState *) (devicePtr->data);
switch (ioctlPtr->command) {
case DEV_CLIENT_START_LIST:
/*
* Write out state of hosts as we know it to user process.
* To do this, we put stuff into the queue starting with a
* DEV_CLIENT_START_LIST and ending with a DEV_CLIENT_END_LIST
* so that the user process knows when it has the whole list.
*/
numEntries = Recov_GetCurrentHostStates(&(statePtr->toUserQueue[1]),
MAX_INDEX - 2);
if (numEntries < 0) {
panic("DevClientStateIOControl: too many hosts for buffer.");
}
statePtr->toUserQueue[0].hostState = DEV_CLIENT_START_LIST;
statePtr->userUnreadIndex = 0;
statePtr->userUnusedIndex = numEntries + 2;
statePtr->toUserQueue[numEntries + 1].hostState = DEV_CLIENT_END_LIST;
break;
case DEV_CLIENT_END_LIST:
/* Show that daemon did read data and poked us about it. */
Sync_Broadcast(&(statePtr->waitForDaemon));
break;
default:
break;
}
return status;
}
/*
*----------------------------------------------------------------------
*
* TimeoutFunc
*
* The user-level daemon hasn't responded quickly enough in writing
* our state to disk. It's probably dead. Since this must all
* happen synchronously, we panic.
*
* Results:
* None.
*
* Side effects:
* May panic.
*
*----------------------------------------------------------------------
*/
static void
TimeoutFunc(clientData, callInfoPtr)
ClientData clientData;
Proc_CallInfo *callInfoPtr;
{
DeviceState *statePtr;
statePtr = (DeviceState *) theDevicePtr->data;
LOCK_MONITOR;
if (!deviceOpen) {
UNLOCK_MONITOR;
return;
}
if (statePtr->userUnreadIndex == currentUnreadIndex) {
printf("Timeout func for host state device called:\n");
printf("\tcurrentUnreadIndex is still %d, userUnreadIndex %d\n",
currentUnreadIndex, statePtr->userUnreadIndex);
UNLOCK_MONITOR;
panic("User-level daemon must be dead.");
}
UNLOCK_MONITOR;
return;
}
/*
*----------------------------------------------------------------------
*
* Dev_ClientHostUp
*
* Tell daemon process to record the fact that another client is
* talking with us.
*
* Results:
* None.
*
* Side effects:
* Talk to user process.
*
*----------------------------------------------------------------------
*/
void
Dev_ClientHostUp(spriteID)
int spriteID;
{
DeviceState *statePtr;
LOCK_MONITOR;
if (!deviceOpen) {
UNLOCK_MONITOR;
return;
}
statePtr = (DeviceState *) theDevicePtr->data;
statePtr->toUserQueue[statePtr->userUnusedIndex].hostNumber = spriteID;
statePtr->toUserQueue[statePtr->userUnusedIndex].hostState =
DEV_CLIENT_STATE_NEW_HOST;
INC_QUEUEPTR(statePtr->userUnusedIndex);
currentUnreadIndex = statePtr->userUnreadIndex;
Fsio_DevNotifyReader(statePtr->dataReadyToken);
/*
* This test is good enough if the daemon promises to read all
* available data whenever it reads data. We also set a timer in
* case the user-level daemon has died and doesn't respond.
*/
Proc_CallFunc(TimeoutFunc, (ClientData) theDevicePtr,
5 * timer_IntOneSecond);
while (statePtr->userUnreadIndex == currentUnreadIndex) {
(void) Sync_Wait(&(statePtr->waitForDaemon), FALSE);
}
UNLOCK_MONITOR;
return;
}
/*
*----------------------------------------------------------------------
*
* Dev_ClientHostDown
*
* Tell daemon process to record the fact that a client that was
* talking with us has died.
*
* Results:
* None.
*
* Side effects:
* Talk to user process.
*
*----------------------------------------------------------------------
*/
void
Dev_ClientHostDown(spriteID)
int spriteID;
{
DeviceState *statePtr;
LOCK_MONITOR;
if (!deviceOpen) {
UNLOCK_MONITOR;
return;
}
statePtr = (DeviceState *) theDevicePtr->data;
statePtr->toUserQueue[statePtr->userUnusedIndex].hostNumber = spriteID;
statePtr->toUserQueue[statePtr->userUnusedIndex].hostState =
DEV_CLIENT_STATE_DEAD_HOST;
INC_QUEUEPTR(statePtr->userUnusedIndex);
currentUnreadIndex = statePtr->userUnreadIndex;
Fsio_DevNotifyReader(statePtr->dataReadyToken);
/*
* This test is good enough if the daemon promises to read all
* available data whenever it reads data. We also set a timer in
* case the user-level daemon has died and doesn't respond.
*/
#ifdef NOTDEF
XX Increase timeout time? Or make timeout function understand that
if client is still marked as going through recovery, it should allow more
time?
#endif NOTDEF
printf("userUnread and currentUnread at %d\n", currentUnreadIndex);
Proc_CallFunc(TimeoutFunc, (ClientData) theDevicePtr,
5 * timer_IntOneSecond);
while (statePtr->userUnreadIndex == currentUnreadIndex) {
(void) Sync_Wait(&(statePtr->waitForDaemon), FALSE);
}
UNLOCK_MONITOR;
return;
}